/*
Copyright (C) 2001, Dylan Menzies
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, 
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
   this list of conditions and the following disclaimer in the documentation and/or 
   other materials provided with the distribution.
 * Neither the name of 'JPhya' nor the names of its contributors may be used 
   to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jphya.impact;

import com.jphya.body.Body;
import com.jphya.resonator.Resonator;
import com.jphya.scene.Scene;
import com.jphya.surface.Surface;

public class Impact {


	private Body body1;
	private Body body2;

	private Resonator resonator1;
	private Resonator resonator2;

	private Surface surface1; // Used to provide a surface type for
	private Surface surface2; // freeing impactGens when the contact is freed.

	private ImpactGenerator impactGen1;
	private ImpactGenerator impactGen2;

	private ImpactDynamicData dynamicData;

	// Impact couplings:
	// These are copied from the owning paBody, and can be altered.

	private float surface1ImpactToRes2Gain;
	private float surface2ImpactToRes1Gain;

	private float surface1ImpactDirectGain;
	private float surface2ImpactDirectGain;

	private boolean isReady; // Used to prevent the audiothread processing the
								// impact before any parameters have been set.

	//private int m_poolHandle; // Handle used by the scene contact manager to
								// release the contact.

	private Object userData;

	protected final Scene scene;
	// Assign acoustic bodies to the contact.
	// Atleast one body should be set otherwise no sound will be generated by
	// the contact!

	public Body getBody1() {
		return body1;
	}

	public Body getBody2() {
		return body2;
	}

	public Resonator getRes1() {
		return getResonator1();
	}

	public Resonator getRes2() {
		return getResonator2();
	}




	public ImpactDynamicData getDynamicData() {
		return dynamicData;
	}

	// Coupling set functions. Use straight after assigning bodies.
	// These couplings overide the coupling setting in the assigned bodies.

	// Master gain factors used at impact generation source.
	public int setSurface1ImpactMasterGain(float s) {
		// assert( ("Body1 has not been assigned with 'setBody1()' ",
		// m_impactGen1) );
		getImpactGen1().setImpactGain(s);
		return 0;
	}

	public int setSurface2ImpactMasterGain(float s) {
		// assert( ("Body2 has not been assigned with 'setBody2()' ",
		// m_impactGen2) );
		getImpactGen2().setImpactGain(s);
		return 0;
	}

	public int setSurface1ImpactPostMasterGainLimit(float s) {
		// assert( ("Body1 has not been assigned with 'setBody1()' ",
		// m_impactGen1) );
		getImpactGen1().setLimit(s);
		return 0;
	}

	public int setSurface2ImpactPostMasterGainLimit(float s) {
		// assert( ("Body2 has not been assigned with 'setBody2()' ",
		// m_impactGen2) );
		getImpactGen2().setLimit(s);
		return 0;
	}

	// These gain factors used in paTick
	public int setSurface1ImpactToRes2Gain(float s) {
		this.surface1ImpactToRes2Gain = s;
		return 0;
	}

	public int setSurface2ImpactToRes1Gain(float s) {
		this.surface2ImpactToRes1Gain = s;
		return 0;
	}

	public int setSurface1ImpactDirectGain(float s) {
		this.surface1ImpactDirectGain = s;
		return 0;
	}

	public int setSurface2ImpactDirectGain(float s) {
		this.surface2ImpactDirectGain = s;
		return 0;
	}

	/*
	 * // Skid coupling. This scales the mixing to resonator1, resonator2 and
	 * direct output. // The balance between these mixes is as for impact.
	 * 
	 * int setSurface1SkidContactGain(float s) { assert(
	 * ("Body1 has not been assigned with 'setBody1()' ", m_impactGen1) );
	 * m_impactGen1.setSkidContactGain(s); return 0; }
	 * 
	 * int setSurface2SkidContactGain(float s) {
	 * assert(("Body2 has not been assigned with 'setBody2()' ", m_impactGen2));
	 * m_impactGen2.setSkidContactGain(s); return 0; }
	 */

	// Regular dynamic data update.

	public boolean getDyanmicDataIsSet() {
		return isReady();
	}

	public Object getUserData() {
		return userData;
	}

	public int setUserData(Object d) {
		userData = d;
		return 0;
	}

	public void initialize() {
		this.setDynamicData(new ImpactDynamicData ());
		setResonator1(null);
		setResonator2(null);
		setImpactGen1(null);
		setImpactGen2(null);
		setSurface1(null);
		setSurface2(null);

		getDynamicData().relTangentSpeedAtImpact = 0;
		getDynamicData().impactImpulse = 0;

		setReady(false);

		
	}
	
	/**
	 * Use the static constructor for allocation management.
	 * @param scene
	 */
	protected Impact(Scene scene) {
		this.scene = scene;
	//	setM_poolHandle(-1);
		initialize();
	//	scene.addImpact(this);
	}

	public void terminate() {
		if (getSurface1() != null)
			getSurface1().deleteImpactGen(getImpactGen1());
		if (getSurface2() != null)
			getSurface2().deleteImpactGen(getImpactGen2());
		
	}

	public static Impact newImpact(Scene scene) {
		// paLOCKCHECK
		
		Impact  i  = scene.getAllocationManager().acquireObject(Impact.class);
		if(i==null)
		{
			i = new Impact(scene);
		}

		//i.setM_poolHandle(Impact.pool.getHandle());

		i.initialize();
		scene.addImpact(i);
		return i;
	}

	public static int deleteImpact(Impact i) {
		// paLOCKCHECK

		// assert(("Impact not valid.", i));
		if (i == null)
			return -1;

		// assert(("Impact not in use.", i.m_poolHandle != -1));
	/*	if (i.getM_poolHandle() == -1)
			return -1; // error - contact not being used.
*/
		i.terminate();
		i.scene.removeImpact(i);
		i.clear();
		i.scene.getAllocationManager().returnObject(i);
		//i.setM_poolHandle(-1);
		return 0;
	}
	/**
	 * Clear outgoing references (except to scene) for returning to the allocation manager.
	 */
	public void clear()
	{
		this.body1 = null;
		this.body2 = null;
		this.dynamicData = null;
		this.impactGen1 = null;
		this.impactGen2 = null;
		this.resonator1= null;
		this.resonator2 =null;
	}
	public int setBody1(Body b) {
		// paLOCKCHECK

		if (b == null)
			return -1;
		if (!b.isEnabled())
			return -1;

		assert (getImpactGen1() == null);
		if (getImpactGen1() != null)
			return -1;

		body1 = b;
		setResonator1(b.getResonator());
		setSurface1(b.getSurface());

		if (getSurface1() == null)
			return -1;

		setImpactGen1(getSurface1().newImpactGen());

		// assert(m_impactGen1);
		if (getImpactGen1() == null) {
			return -1; // No point having an impact.
		}
		;

		// Copy couplings from the body.

		setSurface1ImpactPostMasterGainLimit(b.getImpactAmpLimit());
		setSurface1ImpactMasterGain(b.getImpactMasterGain());
		setSurface1ImpactToRes2Gain(b.getImpactToOtherResGain());
		setSurface1ImpactDirectGain(b.getImpactDirectGain());

		// Let each gen know about the other gen's surface.
		// Used for calculating combined hardness.
		if (getSurface2() != null) {
			getImpactGen2().setOtherSurface(getSurface1());
			getImpactGen1().setOtherSurface(getSurface2());
		}

		return 0;
	}

	public int setBody2(Body b) {
		// paLOCKCHECK

		if (b == null)
			return -1;
		if (!b.isEnabled())
			return -1;

		assert (getImpactGen2() == null);
		if (getImpactGen2() != null)
			return -1;

		body2 = b;
		setResonator2(b.getResonator());
		setSurface2(b.getSurface());

		if (getSurface2() != null)
			return -1;

		setImpactGen2(getSurface2().newImpactGen());

		// assert(m_impactGen2);
		if (getImpactGen2() != null)
			return -1; // No point having a impact.

		// Copy couplings from the body.

		setSurface2ImpactPostMasterGainLimit(b.getImpactAmpLimit());
		setSurface2ImpactMasterGain(b.getImpactMasterGain());
		setSurface2ImpactToRes1Gain(b.getImpactToOtherResGain());
		setSurface2ImpactDirectGain(b.getImpactDirectGain());

		// Let each gen know about the other gen's surface.
		// Used for calculating combined hardness.
		if (getSurface1() != null) {
			getImpactGen2().setOtherSurface(getSurface1());
			getImpactGen1().setOtherSurface(getSurface2());
		}

		return 0;
	}

	public int setDynamicData(ImpactDynamicData d) {
		// paLOCKCHECK

		this.dynamicData = d;
		setReady(true);
		return 0;
	}

	public void setReady(boolean m_isReady) {
		this.isReady = m_isReady;
	}

	public boolean isReady() {
		return isReady;
	}

	public void setResonator1(Resonator m_resonator1) {
		this.resonator1 = m_resonator1;
	}

	public Resonator getResonator1() {
		return resonator1;
	}

	public void setResonator2(Resonator m_resonator2) {
		this.resonator2 = m_resonator2;
	}

	public Resonator getResonator2() {
		return resonator2;
	}

	public void setImpactGen1(ImpactGenerator m_impactGen1) {
		this.impactGen1 = m_impactGen1;
	}

	public ImpactGenerator getImpactGen1() {
		return impactGen1;
	}

	public void setSurface1(Surface m_surface1) {
		this.surface1 = m_surface1;
	}

	public Surface getSurface1() {
		return surface1;
	}

	public void setSurface2(Surface m_surface2) {
		this.surface2 = m_surface2;
	}

	public Surface getSurface2() {
		return surface2;
	}

	private void setImpactGen2(ImpactGenerator m_impactGen2) {
		this.impactGen2 = m_impactGen2;
	}

	public ImpactGenerator getImpactGen2() {
		return impactGen2;
	}




	public float getSurface1ImpactDirectGain() {
		return surface1ImpactDirectGain;
	}



	public float getSurface1ImpactToRes2Gain() {
		return surface1ImpactToRes2Gain;
	}



	public float getSurface2ImpactDirectGain() {
		return surface2ImpactDirectGain;
	}

/*	public void setM_poolHandle(int m_poolHandle) {
		this.m_poolHandle = m_poolHandle;
	}

	public int getM_poolHandle() {
		return m_poolHandle;
	}
*/


	public float getSurface2ImpactToRes1Gain() {
		return surface2ImpactToRes1Gain;
	}

}
